﻿#ifndef TL_STACK_H
#define TL_STACK_H

#include <iostream>
#include "TL_Status.h"


/*
КОДЫ ОШИБОК:
1 - неизвестная ошибка
2 - стек пустой
*/


namespace TL {
    /* TL::Stack */

    template<class T> class StackData {
    public:
        T m_Data;
        StackData<T> *m_Next;
    };

    template<class T> class Stack {
    private:
        StackData<T> *m_Head;
        unsigned int m_Size;

    public:
        Stack();
        Stack(T firstData);
        Stack(const Stack<T> &stack);
        ~Stack();

    public:
        bool IsEmpty() const;
        T Pop(TL::Status *status);
        void Push(T newData);
        unsigned int Size() const;

    public:
        template<class T> friend std::ostream &operator<<(std::ostream &stream, Stack<T> &stack);
    };

    template<class T> Stack<T>::Stack()
    {
        m_Head = 0;
        m_Size = 0;
    }

    template<class T> Stack<T>::Stack(T firstData)
    {
        m_Head = new StackData<T>;
        m_Head->m_Data = firstData;
        m_Head->m_Next = 0;

        m_Size = 1;
    }

    template<class T> Stack<T>::Stack(const Stack<T> &stack)
    {
        if(!stack.IsEmpty()) {
            StackData<T> *pLeft; // указатель для перемещения по левому стеку, указывает на i-1 элемент
            StackData<T> *pRight = stack.m_pHead; // указатель для перемещения по правому стеку, указывает на i элемент

            m_Head = new StackData<T>;
            m_Head->m_Data = pRight->m_Data;

            pLeft = m_Head;
            pRight = pRight->m_Next;

            while(pRight) {
                pLeft->m_Next = new StackData<T>;
                pLeft->m_Next->m_Data = pRight->m_Data;

                pLeft = pLeft->m_Next;
                pRight = pRight->m_Next;
            }

            pLeft->m_Next = 0;
        } else {
            m_Head = 0;
        }

        m_Size = stack.m_Size;
    }

    template<class T> Stack<T>::~Stack()
    {
        StackData<T> *p = m_Head;
        StackData<T> *rem_target;

        while(p) {
            rem_target = p;
            p = p->m_Next;

            delete rem_target;
        }
    }

    template<class T> bool Stack<T>::IsEmpty() const
    {
        return (m_Size == 0);
    }

    template<class T> T Stack<T>::Pop(TL::Status *status)
    {
        status->SetNull();

        if(!m_Size) {
            status->SetError(2);
            return 0;
        }

        T returnVal = m_Head->m_Data;
        StackData<T> *p = m_Head->m_Next;

        delete m_Head;
        m_Head = p;

        m_Size--;

        status->SetSuccess();

        return returnVal;
    }

    template<class T> void Stack<T>::Push(T newData)
    {
        StackData<T> *p = m_Head;

        m_Head = new StackData<T>;
        m_Head->m_Data = newData;
        m_Head->m_Next = p;

        m_Size++;
    }

    template<class T> unsigned int Stack<T>::Size() const
    {
        return m_Size;
    }

    template<class T> std::ostream &operator<<(std::ostream &stream, Stack<T> &stack)
    {
        if(stack.m_Size) {
            StackData<T> *p = stack.m_Head;
            unsigned int i;
            
            stream << "(";
            
            for(i = 0; i < stack.m_Size; i++) {
                stream << p->m_Data << ", ";
                p = p->m_Next;
            }

            stream << "\b\b)";
        }

        return stream;
    }
}

#endif